package com.zkh.myutils.io.log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.zkh.myutils.system.LogConfigBean;
import com.zkh.myutils.utils.StringUtils;
import com.zkh.myutils.utils.UtilsException;

/**
 * 打印流处理类
 * @author zkh
 */
class PrintStreamHandler {
	//打印流处理对象
	private static PrintStreamHandler printStreamHandler = null;
	//构造方法私有化
	private PrintStreamHandler() {}
	
	//当前文件名规则
	private String logFileName;
	private String logFileNameDate;
	private SimpleDateFormat logFileNameSDF;
	private File logFileDir;
	//日志文件写句柄，用来关闭
	private FileWriter fw;
	private BufferedWriter bw;
	//日期格式化
	private SimpleDateFormat sdf = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]");
	
	//日志级别。debug, info, warn, error
	private int logLevel;
	private String[] logClassNames = {"DEBUG", "INFO", "WARN", "ERROR"};
	//日志类型，可选值：console：1,file：2
	private int logType;
	
	//标准流和错误流
	private LogPrintStream standard;
	private LogPrintStream error;
	
	/**
	 * 接收系统打印流
	 */
	public void receiveSystemPrintStream() {
		if(logType==2) {
			System.setOut(standard);
			System.setErr(error);
		}
	}
	
	/**
	 * 获取打印流处理对象
	 * @return
	 */
	public static PrintStreamHandler getPrintStream(LogConfigBean configBean) {
		//为空
		if(printStreamHandler==null) {
			//初始化
			PrintStreamHandler psh = printStreamHandler = new PrintStreamHandler();
			//保存级别
			psh.logLevel = StringUtils.decode(Integer::parseInt, configBean.getLogLevel(), "debug", "0", "info", "1", "warn", "2", "3");
			//保存类型
			psh.logType = "file".equals(configBean.getLogType()) ? 2 : 1;
			try {
				//获取流
				psh.standard = LogPrintStream.getPrintStream(1);
				psh.error = LogPrintStream.getPrintStream(0);
			}catch(IOException e) {e.printStackTrace();}
			//日志类型为文件时，才需要特意处理流
			if(psh.logType>1) {
				//日志文件目录
				psh.logFileDir = configBean.getLogFileDir();
				//获取日志文件名动态日期格式
				String pattern = configBean.getLogFileNameDatePattern();
				//配置日志文件名称（包含日期pattern）
				psh.logFileName = configBean.getLogFileName();
				//当前日志文件名称
				String filename = psh.logFileName;
				//拼接名称
				if(pattern!=null) {
					//日期格式化对象
					psh.logFileNameSDF = new SimpleDateFormat(pattern);
					//规则日期
					psh.logFileNameDate = psh.logFileNameSDF.format(new Date());
					//日志文件名
					filename = psh.logFileName.replace("{" + pattern + "}", psh.logFileNameDate);
				}
				//日志文件
				File logFile = new File(psh.logFileDir, filename + ".txt");
				try {
					//不存在，则创建
					if(!logFile.exists()) {
						logFile.createNewFile();
					}
					//初始化流
					psh.fw = new FileWriter(logFile, true);
					psh.bw = new BufferedWriter(psh.fw);
					//设置文件流
					psh.standard.setBufferedWriter(psh.bw);
					psh.error.setBufferedWriter(psh.bw);
				}catch(IOException e) {
					throw new UtilsException("创建日志文件发生异常");
				}
			}
		}
		//返回
		return printStreamHandler;
	}

	/**
	 * 打印日志并换行
	 * @param level 级别
	 * @param msgs 需要打印的内容
	 */
	public void println(int level, Object[] msgs) {
		//检查日志文件
		checkLogFile();
		//参数为空
		if(msgs==null || msgs.length==0) return;
		//调用路径
		StackTraceElement trace = Thread.currentThread().getStackTrace()[3];
		//前缀
		String prefix = null;
		//按级别处理前缀
		if(level<-2) {
			prefix = "";
		}else if(level<0) {
			prefix = sdf.format(new Date()) + " - [" + trace.getClassName() + "#" + trace.getLineNumber() + "] ";
		}else if(level >= logLevel) {
			prefix = sdf.format(new Date()) + " " + logClassNames[level] + " [" + trace.getClassName() + "#" + trace.getLineNumber() + "] ";
		}
		//输出
		if(prefix!=null) {
			//遍历打印
			for(Object msg: msgs) {
				//获取打印流
				LogPrintStream printStream = level==-4 || level==-2 || level>=2 ? error : standard;
				//如果是异常对象
				if(msg instanceof Throwable) {
					//打印位置
					printStream.println(prefix.substring(0, prefix.length()-1));
					//打印异常明细
					((Throwable) msg).printStackTrace(printStream);
				}else {
					printStream.println(prefix + (msg==null ? "null" : msg.toString()));
				}
			}
		}
	}

	/**
	 * 检查级别。包含关系，如配置文件中为debug，则所有级别返回true，如果配置文件中为error，则只有error返回true
	 * @param level 检查级别
	 * @return
	 */
	public boolean isLevel(int level) {
		return level >= logLevel;
	}

	/**
	 * 关闭资源
	 */
	public void close() {
		try {
			//销毁流句柄
			if(bw!=null) {
				bw.flush();bw.close();bw=null;
			}
			if(fw!=null) {
				fw.close();fw=null;
			}
		}catch(IOException e) {
			System.err.println("关闭日志流时发生异常");
			e.printStackTrace();
		}
	}
	
	/**
	 * 检查日志文件<br>
	 * 如果存在规则名称，首先生成一个临时名称，再检查与当前名称是否相同，如果不相同，说明日志文件已经改变，需要重新生成
	 */
	private void checkLogFile() {
		//存在规则
		if(logFileNameSDF!=null) {
			//生成一个临时名称
			String temp = logFileNameSDF.format(new Date());
			//如果规则文件名不同
			if(!logFileNameDate.equals(temp)) {
				//日志文件名
				String logFileName = this.logFileName.replaceAll("\\{.+\\}", temp);
				//日志文件对象
				File logFile = new File(logFileDir, logFileName + ".txt");
				try {
					//不存在，则创建
					if(!logFile.exists()) {
						logFile.createNewFile();
					}
					//关闭流
					close();
					//初始化流
					fw = new FileWriter(logFile, true);
					bw = new BufferedWriter(fw);
					//重置流
					standard.setBufferedWriter(bw);
					error.setBufferedWriter(bw);
				}catch(IOException e) {
					e.printStackTrace();
					System.out.println("重新生成日志打印流对象失败");
				}
				//保存规则文件名
				logFileNameDate = temp;
			}
		}
	}
}
